<div style="line-height: 1.5em">
    <h2>1 前端自动化构建</h2>
    <p>前端构建工具的作用可以认为是对源项目文件或资源进行文件级处理，将文件或资源处理成需要的最佳输出结构和形式。在处理过程中，我们可以对文件进行模块化引入、依赖分析、资源合并、压缩优化、文件嵌入、路径替换、生成资源包等多种操作，
    这样就能完成很多原本需要手动完成的事情，极大地提高开发效率。</p>
    <h2>2 gulp</h2>
    什么是gulp
    <p>官网定义：基于流的自动化构建工具，如果你查看它的网页源代码，还会看到在<meta>标签里有一段更详细的描述：如果你查看它的网页源代码，还会看到在<meta>标签里有一段更详细的描述：Gulp.js 是一个自动化构建工具，开发者可以使用它在项目开发过程中自动执行常见任务。Gulp.js 是基于 Node.js 构建的，利用 Node.js 流的威力，你可以快速构建项目并减少频繁的 IO 操作。Gulp.js 源文件和你用来定义任务的 Gulp 文件都是通过 JavaScript（或者 CoffeeScript ）源码来实现的。
    流
    在计算机系统中文件的大小是变化很大的，有的文件内容非常多非常大，而 Node.js 里文件操作是异步的，如果用一个形象的比喻来形容将一个文件的内容读取出来并写入另一个文件中，可以将它想象成文件内容像是水流，从一个文件“流”入另一个文件。
    </p>
    <p>在node里面，读写文件可以用“流”来描述：</p>
    <pre style="font-size: 18px;">
        <code>
        "use strict";
        let fs = require("fs");
        fs.createReadStream("./in.txt")
        .pipe(fs.createWriteStream("./out.txt"));
        </code>
    </pre>
    <p>上面的代码除了将 in.txt 文件中的内容输出到 out.txt 之外，不做其他任何事情，相当于复制了一份数据，从语法形式上可以看到，“数据流”从 fs.createReadStream 创建然后经过 pipe 流出，最后到 fs.createWriteStream。
    在这输入流到输出流的中间，我们可以对“流”做一些事情：</p>
    <pre style="font-size: 18px;">
        <code>
    "use strict";
    
    let fs = require("fs");
    let through = require("through2");
    
    fs.createReadStream("./in.txt")
      .pipe(through.obj(function (contents, enc, done) {
        if(enc === "buffer"){
          contents = contents.toString("utf-8");
          enc = "utf-8";
        }
        done(null, contents, enc);
       }))
      .pipe(through.obj(function (contents, enc, done) {
        done(null, contents.toUpperCase(), enc);
       }))
      .pipe(through.obj(function (contents, enc, done) {
        contents = contents.split("").reverse().join("");
        done(null, contents, enc);
       }))
      .pipe(fs.createWriteStream("./out.txt"));
        </code>
    </pre>
    <P>在上面的代码里，我们通过 Node.js 的 through2 库（这是一个针对“流”的包装库），将输入流一步步转换成输出流，在中间的 pipes 中我们先是将 Buffer 转成 String，然后将它变成大写，最后再 reverse 然后传给输出流。
    所以如果 in.txt 的文件内容是 hello world~，那么 out.txt 的文件内容将是： ~DLROW OLLEH。 
    
    中文文档页面除了让你看入门指南、API文档、CLI文档、编写插件文档之外就没什么了，但实际上真要用 Gulp 的高级功能，看英文文档吧！
    </P>
    <h2>Gulp实现</h2>
    <pre style="font-size: 18px;">
        <code>
    安装
    npm install -g gulp
    npm install gulp
    npm install --save-dev gulp
    建立gulpfile.js文件
    目录结构
    ├── gulpfile.js
    ├── node_modules
    │ └── gulp
    └── package.json
        </code>
    </pre>
    <h3>重要api</h3>
    <p>使用gulp，仅需知道5个API即可：<span style="font-size: 20px;color: red;">gulp.task(),gulp.src(),gulp.dest(),gulp.watch(),gulp.run()</span>，所以很容易就能掌握，但有几个地方需理解透彻才行，我会在下面一一说明。
        为了避免出现理解偏差，建议先看一遍官方文档。</p>
    <pre style="font-size: 18px;">
        <code>  
    gulp.src()

    gulp.src(globs[, options])
    globs参数是文件匹配模式(类似正则表达式)，用来匹配文件路径(包括文件名) 
    gulp.src()方法正是用来获取流的，但要注意这个流里的内容不是原始的文件流，而是一个虚拟文件对象流(Vinyl files)，这个虚拟文件对象中存储着原始文件的路径、文件名、内容等信息。
    
    gulp.task()

    方法用来定义任务，
    gulp.task(name[, deps], fn)

    gulp.dest()

    gulp.dest(path[,options])
    gulp.dest()方法是用来写文件的，
    gulp的使用流程一般是这样子的：首先通过gulp.src()方法获取到我们想要处理的文件流，然后把文件流通过pipe方法导入到gulp的插件中，最后把经过插件处理后的流再通过pipe方法导入到gulp.dest()中，gulp.dest()方法则把流中的内容写入到文件中，这里首先需要弄清楚的一点是，我们给gulp.dest()传入的路径参数，只能用来指定要生成的文件的目录，而不能指定生成文件的文件名，它生成文件的文件名使用的是导入到它的文件流自身的文件名，所以生成的文件名是由导入到它的文件流决定的。
    var gulp = require('gulp');
    gulp.src('script/jquery.js')
        .pipe(gulp.dest('dist/foo.js'));
    //最终生成的文件路径为 dist/foo.js/jquery.js,而不是dist/foo.js 

    gulp.watch()

    用来监视文件的变化，当文件发生变化后，我们可以利用它来执行相应的任务，例如文件压缩等。 
    gulp.task('uglify',function(){ //do something }); gulp.task('reload',function(){ //do something }); 
    gulp.watch('js/**/*.js', ['uglify','reload']);

    gulp.run()

    </code></pre>
    <h3>基础</h3>
    <pre style="font-size: 18px;">
        <code>
                var gulp = require("gulp");
    
                gulp.task("sync1", function() {
                  console.log("我是一个同步任务");
                });
                
                gulp.task("async", function(done) {
                  setTimeout(function(){
                    console.log("我是一个异步任务");
                    done();
                  }, 2000);  
                }); 
        </code>
    </pre>

    <p>Gulp 的任务可以是同步和异步，在异步任务中确定任务完成，可以通过调用函数参数 done 来实现。
            组合任务</p> 
    <pre style="font-size: 18px;">
            <code>
                    var gulp = require("gulp");
                    var through = require("through2");
                    
                    gulp.task("sync1", function() {
                      console.log("我是一个同步任务");
                    });
                    
                    gulp.task("sync2", function() {
                      console.log("我是另一个同步任务");
                    });
                    
                    gulp.task("sync3", function() {
                      console.log("我是又一个同步任务");
                    });
                    
                    gulp.task("async", function(done) {
                      console.log("老大喊我去搬砖");
                      setTimeout(function(){
                        console.log("我是一个异步任务");
                        done();
                      }, 2000);  
                    });
                    
                    gulp.task("syncs", ["async", "sync1", "sync2", "sync3"], 
                      function(){
                        console.log("砖搬完了！");
                      }); 
            </code>
        </pre>
    <p>我们看到说 gulp.task 可以有依赖，只要第二个参数传一个数组，中间加上依赖的任务就行了，而数组里面的这些任务是
        <span style="color: red;">并行处理</span>
    </p>
        
    <h3>通配符</h3>
    <pre style="font-size: 18px;">
        <code>
            gulp.task("src-dist", function(){
              gulp.src("./**/*.html")
                  .pipe(gulp.dest("./dist"));
            }); 
        </code>
    </pre>
    <p>
        这样处理的 html 文件不仅仅匹配当前目录下的，还包括所有子目录里。关于输入这块，具体的用法还有很多，<span style="color: red;">遵循的规范是glob模式。</span>
        https://github.com/isaacs/node-glob
    </p>

    <h3>Gulp优点</h3>
    <ul>
        <li>
                易于使用
                通过代码优于配置的策略，Gulp 让简单的任务简单，复杂的任务可管理。
        </li>
        <li>
            构建快速
            利用 Node.js 流的威力，你可以快速构建项目并减少频繁的 IO 操作。

        </li>
        <li>
            插件高质
            Gulp 严格的插件指南确保插件如你期望的那样简洁高质得工作。

        </li>
        <li>
            易于学习
            通过最少的 API，掌握 Gulp 毫不费力，构建工作尽在掌握：如同一系列流管道。 

        </li>
    </ul>
    
    <h2>gulp应用</h2>
    <h3>1 常用插件</h3>
    <pre style="font-size: 18px;">
        <code>
            * browser-sync
            * run-sequence
            * gulp-htmlmin
            * gulp-less
            * gulp-autoprefixer
            * gulp-sourcemaps
            * gulp-cssnano
            * gulp-uglify
            * gulp-imagemin
            * gulp-util
        </code>
    </pre>
    <h3>2 gulp、wenpack和parcel比较</h3>
    <h3>Gulp/wenpack</h3>
    <p>Gulp 的定位是 Task Runner, 就是用来跑一个一个任务的。自动化这个流程Grunt/Gulp, 比如你写完代码后要想发布production版本，用一句 gulp build 就可以自动把sass编译成css, coffee编译成js压缩各自的文件，压缩图片，生成图片sprite拷贝minified/uglified 文件到 dist 文件夹。
    但是它没发解决的是 js module 的问题，是你写代码时候如何组织代码结构的问题.
    之前大家可以用 require.js, sea.js 来 require dependency, 后来出了一个 webpack 说 我们能不能把所有的文件(css, image, js) 都用 js 来 生成依赖，最后生成一个bundle呢？ 所以webpack 也叫做file bundler.同时 webpack 为了解决可以 require 不同文件的需求引入了loader, 比如面对sass文件有sass-loader, 把sass 转换成 csscss-loader, 让 webpack 能识别处理 cssstyle-loader, 把识别后的 css 插入到 html style中类似的识别es6 有babel-loader
    本来这就是 webpack 的初衷，require everything, bundle everything. 一开始 webpack 刚出来的时候大家都是把它结合着 gulp 一起用的， gulp 里面有个 gulp-webpack，就是让 webpack 专门去做module dependency的事情, 生成一个bundle.js文件，然后再用 gulp 去做一些其他杂七杂八minify, uglify的事情。 后来人们发现 webpack 有个plugins的选项， 可以用来进一步处理经过loader 生成的bundle.js，于是有人写了对应的插件， 所以minify/uglify, 生成hash的工作也可以转移到webpack本身了，挤掉了gulp这部分的市场份额。 再后来大家有发现 npm/package.json 里面的scripts 原来好好用啊，调用任务的时候就直接写一个简单的命令，因为 gulp 也不就是各种插件命令的组合呀，大部分情况下越来越不需要 gulp/grunt 之类的了 ref. 所以你现在看到的很多新项目都是package.json里面scripts 写了一堆，外部只需要一个webpack就够了。
    打个不恰当的比方，webpack就像微信一样，本来就是做聊天(module dependency)的，后来生生搞出一个微信小程序(processing files)，大家面对简单的需求发现这个比原生app方便使用啊，于是开发原生的人越来越少一样。
    </p>
    <h3>Webpack/parcel</h3>
    <p>以下是 Parcel 让我心动的点：</p>   
    <ul>
        <li>能做到无配置完成以上项目构建要求</li>
        <li>内置了常见场景的构建方案及其依赖，无需再安装各种依赖</li>
        <li>能以 HTML 为入口，自动检测和打包依赖资源</li>
        <li>默认支持模块热替换，真正的开箱即用</li>
    </ul>
    <p>而反观 Webpack，比 Parcel 要麻烦很多:</p>
    <ul>
        <li>需要写一堆配置</li>
        <li>需要再安装一堆依赖</li>
        <li>不能简单的自动生成 HTML</li>
    </ul>
</div>